package ar.edu.itba.pod.legajo47573;

import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;

import ar.edu.itba.event.EventInformation;
import ar.edu.itba.event.RemoteEventDispatcher;
import ar.edu.itba.node.Node;
import ar.edu.itba.node.NodeInformation;
import ar.edu.itba.node.api.ClusterAdministration;
import ar.edu.itba.pod.agent.runner.Agent;
import ar.edu.itba.pod.multithread.EventDispatcher;

public class RemoteEventDispatcherImpl implements RemoteEventDispatcher {
	
	private MyNode currentNode;
	private EventInformation lastEvent;
	
	public RemoteEventDispatcherImpl(MyNode currentNode) throws RemoteException{
		Registry registry = LocateRegistry.createRegistry(currentNode.getInfo().port());
		try {
			registry.bind(Node.DISTRIBUTED_EVENT_DISPATCHER, this);
		} catch (AlreadyBoundException e) {
			e.printStackTrace();
		}
		this.currentNode = currentNode;
	}
	
	/*Tiene que ser broadcast, debería publicar el evento a todos los demás nodos*/
	@Override
	public void publish(EventInformation event) throws RemoteException,
			InterruptedException {
		/*Publico el evento a mi*/
		EventDispatcher localDispatcher = ((MyLocalSimulation)this.currentNode.getSimulation()).dispatcher();
		//Si el evento es el último que me pasaron entonces no lo agrego
		/*TODO esto no sirve del todo cuando hay varios nodos xq un evento puede quedar haciendo broadcast
		y recibir otro y despues recibir el que quedó dando vueltas de nuevo, hay que tener una lista de eventos aca??*/
		if(this.lastEvent.equals(event))
			return;
		localDispatcher.publish(event.source(), event.event().getClass());
		this.lastEvent = event;
		/*Publico el evento a los demás*/
		Set<NodeInformation> nodes = new HashSet<NodeInformation>();
		nodes.addAll(((ClusterAdministrationImpl) currentNode.getCluster()).getKnownNodes());
		
		for(NodeInformation it : nodes){
			Registry remoteRegistry = LocateRegistry.getRegistry(it.host(), it.port());
			RemoteEventDispatcher stub;
			try {
				stub = (RemoteEventDispatcher)remoteRegistry.lookup(Node.DISTRIBUTED_EVENT_DISPATCHER);
				stub.publish(event);
			} catch (NotBoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/*TODO preguntar si devuelve los eventos del nodeInformation que tengo yo nomás o
	 * si hay que broadcastear e ir recogiendo lo de todos los demás nodos
	 */
	@Override
	public Set<EventInformation> newEventsFor(NodeInformation nodeInformation)
			throws RemoteException {
		return null;
	}

	@Override
	public BlockingQueue<Object> moveQueueFor(Agent agent)
			throws RemoteException {
		// TODO Auto-generated method stub
		return null;
	}

}
